package service

import (
	"context"
	"fmt"
	"log"

	"github.com/devops-smartbot/devops-smartbot/services/notification-service/internal/channels"
	"github.com/devops-smartbot/devops-smartbot/services/notification-service/internal/models"
	"github.com/devops-smartbot/devops-smartbot/services/notification-service/internal/repository"
	"go.mongodb.org/mongo-driver/bson"
)

type NotificationService struct {
	repo           *repository.NotificationRepository
	emailChannel   *channels.EmailChannel
	slackChannel   *channels.SlackChannel
	maxRetries     int
}

func NewNotificationService(
	repo *repository.NotificationRepository,
	emailConfig channels.EmailConfig,
	slackConfig channels.SlackConfig,
) *NotificationService {
	return &NotificationService{
		repo:         repo,
		emailChannel: channels.NewEmailChannel(emailConfig),
		slackChannel: channels.NewSlackChannel(slackConfig),
		maxRetries:   3,
	}
}

// SendNotification sends a notification through specified channel
func (s *NotificationService) SendNotification(ctx context.Context, notification *models.Notification) (string, error) {
	// Create notification record
	if err := s.repo.CreateNotification(ctx, notification); err != nil {
		return "", fmt.Errorf("failed to create notification: %w", err)
	}

	// Send notification asynchronously
	go s.sendAsync(notification)

	return notification.ID, nil
}

// sendAsync sends notification in background
func (s *NotificationService) sendAsync(notification *models.Notification) {
	ctx := context.Background()

	var err error
	switch notification.Channel {
	case models.ChannelEmail:
		err = s.emailChannel.Send(notification.Recipients, notification.Subject, notification.Body)
	case models.ChannelSlack:
		err = s.slackChannel.Send(notification.Recipients, notification.Subject, notification.Body)
	default:
		err = fmt.Errorf("unsupported channel: %s", notification.Channel)
	}

	if err != nil {
		log.Printf("Failed to send notification %s: %v", notification.ID, err)
		s.repo.UpdateNotificationStatus(ctx, notification.ID, models.StatusFailed, err.Error())
	} else {
		log.Printf("Successfully sent notification %s via %s", notification.ID, notification.Channel)
		s.repo.UpdateNotificationStatus(ctx, notification.ID, models.StatusSent, "")
	}
}

// RetryFailedNotifications retries failed notifications
func (s *NotificationService) RetryFailedNotifications(ctx context.Context) error {
	notifications, err := s.repo.GetPendingNotifications(ctx, s.maxRetries)
	if err != nil {
		return fmt.Errorf("failed to get pending notifications: %w", err)
	}

	log.Printf("Retrying %d pending notifications", len(notifications))

	for _, notification := range notifications {
		// Update status to retrying
		s.repo.UpdateNotificationStatus(ctx, notification.ID, models.StatusRetrying, "")

		// Retry sending
		go s.sendAsync(notification)
	}

	return nil
}

// GetNotificationHistory retrieves notification history with filters
func (s *NotificationService) GetNotificationHistory(ctx context.Context, filters map[string]interface{}, page, pageSize int) ([]*models.Notification, int, error) {
	// Build MongoDB filters
	bsonFilters := bson.M{}

	if source, ok := filters["source"].(string); ok && source != "" {
		bsonFilters["source"] = source
	}

	if sourceID, ok := filters["sourceId"].(string); ok && sourceID != "" {
		bsonFilters["sourceId"] = sourceID
	}

	if channel, ok := filters["channel"].(models.Channel); ok && channel != "" {
		bsonFilters["channel"] = channel
	}

	if status, ok := filters["status"].(models.Status); ok && status != "" {
		bsonFilters["status"] = status
	}

	return s.repo.GetNotifications(ctx, bsonFilters, page, pageSize)
}

// GetUserPreferences retrieves user notification preferences
func (s *NotificationService) GetUserPreferences(ctx context.Context, userID string) (*models.UserPreferences, error) {
	return s.repo.GetUserPreferences(ctx, userID)
}

// UpdateUserPreferences updates user notification preferences
func (s *NotificationService) UpdateUserPreferences(ctx context.Context, prefs *models.UserPreferences) error {
	return s.repo.UpdateUserPreferences(ctx, prefs)
}

// ShouldSendNotification checks if notification should be sent based on user preferences
func (s *NotificationService) ShouldSendNotification(ctx context.Context, userID string, channel models.Channel, eventType string) (bool, error) {
	prefs, err := s.repo.GetUserPreferences(ctx, userID)
	if err != nil {
		return false, err
	}

	// Check channel enabled
	switch channel {
	case models.ChannelEmail:
		if !prefs.EmailEnabled {
			return false, nil
		}
	case models.ChannelSlack:
		if !prefs.SlackEnabled {
			return false, nil
		}
	case models.ChannelDingTalk:
		if !prefs.DingTalkEnabled {
			return false, nil
		}
	}

	// Check event subscription
	if subscribed, ok := prefs.EventSubscriptions[eventType]; ok && !subscribed {
		return false, nil
	}

	return true, nil
}
